{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Docstrings and Annotations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Docstrings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "When we call **help()** on a class, function, module, etc, Python will typically display some information:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function print in module builtins:\n",
      "\n",
      "print(...)\n",
      "    print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n",
      "    \n",
      "    Prints the values to a stream, or to sys.stdout by default.\n",
      "    Optional keyword arguments:\n",
      "    file:  a file-like object (stream); defaults to the current sys.stdout.\n",
      "    sep:   string inserted between values, default a space.\n",
      "    end:   string appended after the last value, default a newline.\n",
      "    flush: whether to forcibly flush the stream.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(print)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can define such help using docstrings and annotations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def my_func(a, b):\n",
    "    return a*b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function my_func in module __main__:\n",
      "\n",
      "my_func(a, b)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(my_func)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pretty bare! So let's add some additional help:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def my_func(a, b):\n",
    "    'Returns the product of a and b'\n",
    "    return a*b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function my_func in module __main__:\n",
      "\n",
      "my_func(a, b)\n",
      "    Returns the product of a and b\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(my_func)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Docstrings can span multiple lines using a multi-line string literal:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def fact(n):\n",
    "    '''Calculates n! (factorial function)\n",
    "    \n",
    "    Inputs:\n",
    "        n: non-negative integer\n",
    "    Returns:\n",
    "        the factorial of n\n",
    "    '''\n",
    "    \n",
    "    if n < 0:\n",
    "        '''Note that this is not part of the docstring!'''\n",
    "        return 1\n",
    "    else:\n",
    "        return n * fact(n-1)\n",
    "    \n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function fact in module __main__:\n",
      "\n",
      "fact(n)\n",
      "    Calculates n! (factorial function)\n",
      "    \n",
      "    Inputs:\n",
      "        n: non-negative integer\n",
      "    Returns:\n",
      "        the factorial of n\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(fact)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Docstrings, when found, are simply attached to the function in the `__doc__` property:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Calculates n! (factorial function)\\n    \\n    Inputs:\\n        n: non-negative integer\\n    Returns:\\n        the factorial of n\\n    '"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fact.__doc__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And the Python **help()** function simply returns the contents of `__doc__`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Annotations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also add metadata annotations to a function's parameters and return. These metadata annotations can be any **expression** (string, type, function call, etc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def my_func(a:'annotation for a', \n",
    "            b:'annotation for b')->'annotation for return':\n",
    "    \n",
    "    return a*b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function my_func in module __main__:\n",
      "\n",
      "my_func(a:'annotation for a', b:'annotation for b') -> 'annotation for return'\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(my_func)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The annotations can be any expression, not just strings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "x = 3\n",
    "y = 5\n",
    "def my_func(a: str) -> 'a repeated ' + str(max(3, 5)) + ' times':\n",
    "\treturn a*max(x, y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function my_func in module __main__:\n",
      "\n",
      "my_func(a:str) -> 'a repeated 5 times'\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(my_func)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that these annotations do **not** force a type on the parameters or the return value - they are simply there for documentation purposes within Python and **may** be used by external applications and modules, such as IDE's."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Just like docstrings are stored in the `__doc__` property, annotations are stored in the `__annotations__` property - a dictionary whose keys are the parameter names, and values are the annotation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'a': str, 'return': 'a repeated 5 times'}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func.__annotations__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Of course we can combine both docstrings and annotations:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def fact(n: 'int >= 0')->int:\n",
    "    '''Calculates n! (factorial function)\n",
    "    \n",
    "    Inputs:\n",
    "        n: non-negative integer\n",
    "    Returns:\n",
    "        the factorial of n\n",
    "    '''\n",
    "    \n",
    "    if n < 0:\n",
    "        '''Note that this is not part of the docstring!'''\n",
    "        return 1\n",
    "    else:\n",
    "        return n * fact(n-1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function fact in module __main__:\n",
      "\n",
      "fact(n:'int >= 0') -> int\n",
      "    Calculates n! (factorial function)\n",
      "    \n",
      "    Inputs:\n",
      "        n: non-negative integer\n",
      "    Returns:\n",
      "        the factorial of n\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(fact)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Annotations will work with default parameters too: just specify the default **after** the annotation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def my_func(a:str='a', b:int=1)->str:\n",
    "    return a*b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function my_func in module __main__:\n",
      "\n",
      "my_func(a:str='a', b:int=1) -> str\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(my_func)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'a'"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'abcabcabc'"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func('abc', 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def my_func(a:int=0, *args:'additional args'):\n",
    "    print(a, args)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'a': int, 'args': 'additional args'}"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func.__annotations__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function my_func in module __main__:\n",
      "\n",
      "my_func(a:int=0, *args:'additional args')\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(my_func)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
